package com.tian.service.impl;

import com.alibaba.fastjson.JSON;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.tian.common.CommonResult;
import com.tian.dto.*;
import com.tian.entity.ChargeStationGun;
import com.tian.entity.ChargeStationInfo;
import com.tian.enums.ChargingSpeedType;
import com.tian.enums.ChargingStationStatusEnum;
import com.tian.enums.ResultCode;
import com.tian.mapper.ChargeStationGunMapper;
import com.tian.mapper.ChargeStationInfoMapper;
import com.tian.service.ChargeStationService;
import com.tian.util.*;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.*;
import org.redisson.api.geo.GeoSearchArgs;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * {@code @description:} 充电站
 *
 * @author tianwc 公众号：Java后端技术全栈
 * 在线刷题 1200+java面试题和1000+篇技术文章：<a href="https://woaijava.cc/">博客地址</a>
 * {@code @date:} 2024/1/18 18:52
 * {@code @version:} 1.0
 */
@Slf4j
@Service
public class ChargeStationServiceImpl implements ChargeStationService {
    @Resource
    private ChargeStationInfoMapper chargeStationInfoMapper;
    @Resource
    private RedissonClient redissonClient;
    @Resource
    private ChargeStationGunMapper chargeStationGunMapper;


    @Override
    public String test(String msg) {
        List<ChargeStationInfo> chargeStationInfos = chargeStationInfoMapper.selectAll();
        log.info("station demo={}", chargeStationInfos.size());
        return "station impl resp:" + msg;
    }


    @Override
    public CommonResult<Boolean> add(StationDto stationDto) {
        int count = chargeStationInfoMapper.selectStation(stationDto);
        if (count > 0) {
            return CommonResult.failed("该充电站信息已入库");
        }
        ChargeStationInfo chargeStationInfo = new ChargeStationInfo();
        BeanUtils.copyProperties(stationDto, chargeStationInfo);
        chargeStationInfo.setCreateTime(new Date());
        chargeStationInfo.setStatus(ChargingStationStatusEnum.BUILDING.getStatus());

        //初始化 设置默认值
        chargeStationInfo.setBusinessStartTime(LocalTime.of(0, 0, 1));
        chargeStationInfo.setBusinessEndTime(LocalTime.of(23, 59, 59));
        chargeStationInfo.setChargingSpeedType(ChargingSpeedType.MIXED_CHARGING.getSpeed());
        chargeStationInfo.setParkFee(0);
        chargeStationInfo.setLounge(0);
        chargeStationInfo.setToilet(0);
        chargeStationInfo.setShoppingMall(0);
        chargeStationInfo.setWashCar(0);
        chargeStationInfo.setWashFee(BigDecimal.ZERO);
        chargeStationInfo.setDiet(0);
        chargeStationInfo.setLongitude(BigDecimal.ZERO);
        chargeStationInfo.setLatitude(BigDecimal.ZERO);

        chargeStationInfoMapper.insert(chargeStationInfo);
        RBucket<String> gunBucket = redissonClient.getBucket(StationRedisPreKey.STATION_PRE_KEY + chargeStationInfo.getId());
        gunBucket.set(JSON.toJSONString(chargeStationInfo));
        return CommonResult.success(Boolean.TRUE);
    }

    @Override
    public CommonResult<Boolean> update(StationDto stationDto) {
        ChargeStationInfo record = chargeStationInfoMapper.selectByPrimaryKey(stationDto.getId());
        record.setId(stationDto.getId());
        record.setStationName(StringUtil.isBlank(stationDto.getStationName()) ? record.getStationName() : stationDto.getStationName());
        record.setAddress(StringUtil.isBlank(stationDto.getAddress()) ? record.getAddress() : stationDto.getAddress());
        record.setDedicatedType(stationDto.getDedicatedType());
        record.setChargingSpeedType(stationDto.getChargingSpeedType());
        record.setOriginalPrice(stationDto.getOriginalPrice());
        record.setPrice(stationDto.getPrice());
        chargeStationInfoMapper.updateByPrimaryKey(record);
        RBucket<String> gunBucket = redissonClient.getBucket(StationRedisPreKey.STATION_PRE_KEY + record.getId());
        gunBucket.set(JSON.toJSONString(record));
        return CommonResult.success(Boolean.TRUE);
    }

    @Override
    public CommonResult<Boolean> updateDetail(StationDto stationDto) {
        ChargeStationInfo record = new ChargeStationInfo();
        record.setId(stationDto.getId());

        record.setChargingSpeedType(stationDto.getChargingSpeedType());

        record.setParkFee(stationDto.getParkFee());
        record.setLounge(stationDto.getLounge());
        record.setToilet(stationDto.getToilet());
        record.setShoppingMall(stationDto.getShoppingMall());
        record.setWashCar(stationDto.getWashCar());
        record.setWashFee(stationDto.getWashFee());
        record.setBusinessStartTime(stationDto.getBusinessStartTime());
        record.setBusinessEndTime(stationDto.getBusinessEndTime());
        record.setDiet(stationDto.getDiet());
        record.setLongitude(stationDto.getLongitude());
        record.setLatitude(stationDto.getLatitude());
        record.setCreateTime(stationDto.getCreateTime());
        chargeStationInfoMapper.updateByPrimaryKey(record);
        RBucket<String> gunBucket = redissonClient.getBucket(StationRedisPreKey.STATION_PRE_KEY + record.getId());
        gunBucket.set(JSON.toJSONString(record));
        return CommonResult.success(Boolean.TRUE);
    }

    @Override
    public CommonResult<Boolean> start(Integer id) {
        StationDto stationDto = new StationDto();
        stationDto.setId(id);
        stationDto.setStatus(ChargingStationStatusEnum.OPERATING.getStatus());
        chargeStationInfoMapper.updateStatusById(stationDto);

        /*
         * 保存到redis中 以及到ES中，就是个分布式事务问题，我们可以采取定时任务或者MQ来解决
         */
        ChargeStationInfo chargeStationInfo = chargeStationInfoMapper.selectByPrimaryKey(id);
        RBucket<String> gunBucket = redissonClient.getBucket(StationRedisPreKey.STATION_PRE_KEY + stationDto.getId());
        gunBucket.set(JSON.toJSONString(chargeStationInfo));
        //添加到redis中 用于地图查询
        RGeo<ChargeStationInfo> geo = redissonClient.getGeo("station-geo");
        geo.add(chargeStationInfo.getLongitude().doubleValue(), chargeStationInfo.getLatitude().doubleValue(), chargeStationInfo);
        return CommonResult.success(Boolean.TRUE);
    }

    @Override
    public CommonResult<Boolean> bind(StationDto stationDto) {
        ChargeStationInfo record = new ChargeStationInfo();
        record.setId(stationDto.getId());
        record.setUserId(stationDto.getUserId());
        record.setUserName(stationDto.getUserName());
        chargeStationInfoMapper.updateByPrimaryKey(record);
        RBucket<String> gunBucket = redissonClient.getBucket(StationRedisPreKey.STATION_PRE_KEY + record.getId());
        gunBucket.set(JSON.toJSONString(record));
        return CommonResult.success(Boolean.TRUE);
    }

    @Override
    public CommonResult<Boolean> stop(Integer id) {
        RBucket<String> gunBucket = redissonClient.getBucket(StationRedisPreKey.STATION_PRE_KEY + id);
        gunBucket.delete();
        ChargeStationInfo chargeStationInfo = chargeStationInfoMapper.selectByPrimaryKey(id);
        chargeStationInfo.setStatus(ChargingStationStatusEnum.STOPPING.getStatus());
        chargeStationInfoMapper.updateByPrimaryKey(chargeStationInfo);
        return CommonResult.success(Boolean.TRUE);
    }

    @Override
    public CommonResult<StationRespDto> findById(Integer id) {
        RBucket<String> gunBucket = redissonClient.getBucket(StationRedisPreKey.STATION_PRE_KEY + id);
        String stationInfo = gunBucket.get();
        if (stationInfo != null) {
            ChargeStationInfo chargeStationInfo = JSON.parseObject(stationInfo, ChargeStationInfo.class);
            StationRespDto stationRespDto = new StationRespDto();
            BeanUtils.copyProperties(chargeStationInfo, stationRespDto);
            return CommonResult.success(stationRespDto);
        }
        ChargeStationInfo chargeStationInfo = chargeStationInfoMapper.selectByPrimaryKey(id);
        if (chargeStationInfo != null) {
            chargeStationInfo = chargeStationInfoMapper.selectByPrimaryKey(id);
            StationRespDto stationRespDto = new StationRespDto();
            BeanUtils.copyProperties(chargeStationInfo, stationRespDto);
            gunBucket.set(JSON.toJSONString(chargeStationInfo));
            return CommonResult.success(stationRespDto);
        }
        return CommonResult.failed(ResultCode.PARAMETER_ERROR, "充电站id=" + id + "不存在");
    }

    @Override
    public CommonResult<GunRespDto> findByGunId(Integer id) {
        ChargeStationGun chargeStationGun = chargeStationGunMapper.selectByPrimaryKey(id);
        if (chargeStationGun != null) {
            GunRespDto gunRespDto = new GunRespDto();
            BeanUtils.copyProperties(chargeStationGun, gunRespDto);
            return CommonResult.success(gunRespDto);
        }
        return CommonResult.failed(ResultCode.PARAMETER_ERROR, "充电枪id=" + id + "不存在");
    }

    @Override
    public CommonResult<StationQueryPageRespDto> listByPage(ChargeStationQueryPageReqDto chargeStationQueryPageReqDto, Boolean isApi) {

        chargeStationQueryPageReqDto.setStart((chargeStationQueryPageReqDto.getCurrentPage() - 1) * chargeStationQueryPageReqDto.getPageSize());
        int total = chargeStationInfoMapper.count(chargeStationQueryPageReqDto);
        if (total == 0) {
            StationQueryPageRespDto stationQueryPageRespDto = new StationQueryPageRespDto();
            stationQueryPageRespDto.setPageSize(chargeStationQueryPageReqDto.getPageSize());
            stationQueryPageRespDto.setCurrentPage(chargeStationQueryPageReqDto.getCurrentPage());
            stationQueryPageRespDto.setTotal(total);
            return CommonResult.success(stationQueryPageRespDto);
        }
        List<ChargeStationInfo> chargeStationInfos = chargeStationInfoMapper.listByPage(chargeStationQueryPageReqDto);
        StationQueryPageRespDto stationQueryPageRespDto = new StationQueryPageRespDto();
        List<StationRespDto> data = new ArrayList<>();
        for (ChargeStationInfo chargeStationInfo : chargeStationInfos) {
            StationRespDto stationRespDto = new StationRespDto();
            BeanUtils.copyProperties(chargeStationInfo, stationRespDto);
            stationRespDto.setCreateTimeStr(DateUtils.format(chargeStationInfo.getCreateTime(), DateUtils.FORMAT_SECOND));

            if (isApi){
               doOther(stationRespDto, chargeStationInfo,chargeStationQueryPageReqDto);
            }

            data.add(stationRespDto);
        }
        stationQueryPageRespDto.setPageSize(chargeStationQueryPageReqDto.getPageSize());
        stationQueryPageRespDto.setCurrentPage(chargeStationQueryPageReqDto.getCurrentPage());
        stationQueryPageRespDto.setTotal(total);
        stationQueryPageRespDto.setData(data);

        return CommonResult.success(stationQueryPageRespDto);
    }

    private void doOther(StationRespDto stationRespDto, ChargeStationInfo chargeStationInfo,ChargeStationQueryPageReqDto chargeStationQueryPageReqDto) {
        stationRespDto.setDistance(distanceDecimal(chargeStationInfo, chargeStationQueryPageReqDto).toString());
        stationRespDto.setDistanceUnit(GeoUnit.KILOMETERS.toString());

        //统计充电桩数量
        String availableKey = "station_" + chargeStationInfo.getId() + ":available";
        int availableCount = redissonClient.getSet(availableKey).size();
        stationRespDto.setAvailableGuns(availableCount);

        String unavailableKey = "station_" + chargeStationInfo.getId() + ":unavailable";
        int unavailableCount = redissonClient.getSet(unavailableKey).size();
        stationRespDto.setUnavailableGuns(unavailableCount);

        stationRespDto.setCountGuns(availableCount + unavailableCount);
    }

    @Override
    public CommonResult<StationQueryPageRespDto> listByPageApi(ChargeStationQueryPageReqDto chargeStationQueryPageReqDto) {
        if (StringUtil.isBlank(chargeStationQueryPageReqDto.getStationName())) {
            RGeo<ChargeStationInfo> geo = redissonClient.getGeo("station-geo");
            long count = geo.stream().count();
            if (count == 0) {
                StationQueryPageRespDto stationQueryPageRespDto = new StationQueryPageRespDto();
                stationQueryPageRespDto.setPageSize(chargeStationQueryPageReqDto.getPageSize());
                stationQueryPageRespDto.setCurrentPage(chargeStationQueryPageReqDto.getCurrentPage());
                stationQueryPageRespDto.setTotal(0);
                return CommonResult.success(stationQueryPageRespDto);
            }
            double longitude = chargeStationQueryPageReqDto.getLongitude().doubleValue();
            double latitude = chargeStationQueryPageReqDto.getLatitude().doubleValue();
            int querySize = count > 10 ? 10 : (int) count;
            List<ChargeStationInfo> chargeStationInfos = geo.search(GeoSearchArgs.from(longitude, latitude)
                    .radius(20, GeoUnit.KILOMETERS)
                    .order(GeoOrder.ASC)
                    .count(querySize));

            StationQueryPageRespDto stationQueryPageRespDto = new StationQueryPageRespDto();
            List<StationRespDto> data = new ArrayList<>();
            for (ChargeStationInfo chargeStationInfo : chargeStationInfos) {
                StationRespDto stationRespDto = new StationRespDto();
                BeanUtils.copyProperties(chargeStationInfo, stationRespDto);
                stationRespDto.setCreateTimeStr(DateUtils.format(chargeStationInfo.getCreateTime(), DateUtils.FORMAT_SECOND));
                doOther(stationRespDto, chargeStationInfo,chargeStationQueryPageReqDto);
                data.add(stationRespDto);
            }
            stationQueryPageRespDto.setPageSize(chargeStationQueryPageReqDto.getPageSize());
            stationQueryPageRespDto.setCurrentPage(chargeStationQueryPageReqDto.getCurrentPage());
            stationQueryPageRespDto.setTotal((int) count);
            stationQueryPageRespDto.setData(data);

            return CommonResult.success(stationQueryPageRespDto);
        }
        return this.listByPage(chargeStationQueryPageReqDto,true);
    }

    private Double distanceDecimal(ChargeStationInfo chargeStationInfo, ChargeStationQueryPageReqDto chargeStationQueryPageReqDto) {
        double distance = DistanceCalculateUtil.getDistance(chargeStationInfo.getLongitude().doubleValue(), chargeStationInfo.getLatitude().doubleValue(),
                chargeStationQueryPageReqDto.getLongitude().doubleValue(), chargeStationQueryPageReqDto.getLatitude().doubleValue());
        return new BigDecimal(distance).setScale(3, BigDecimal.ROUND_HALF_UP).doubleValue();
    }

    @Override
    public CommonResult<GunRespDto> getRandomAvailable() {
        //
        ChargeStationGun chargeStationGun = chargeStationGunMapper.getRandomAvailable();
        if (chargeStationGun != null) {
            GunRespDto gunRespDto = new GunRespDto();
            BeanUtils.copyProperties(chargeStationGun, gunRespDto);
            return CommonResult.success(gunRespDto);
        }
        return CommonResult.failed(ResultCode.PARAMETER_ERROR, "暂无可用充电桩");
    }

    @Override
    public CommonResult<BaseRespPageDto<List<MyStationRespDto>>> myStationList(BaseReqPageDto baseReqPageDto) {
        ChargeUserLoginResDto user = UserCacheUtil.getUser();
        Long userId = user.getId();
        Page<ChargeStationInfo> page = PageHelper.startPage(baseReqPageDto.getPageNum(), baseReqPageDto.getPageSize());
        List<ChargeStationInfo> chargeStationInfos = chargeStationInfoMapper.selectByUserId(userId);
        BaseRespPageDto<List<MyStationRespDto>> baseRespPageDto = new BaseRespPageDto<>();

        List<MyStationRespDto> data = new ArrayList<>();
        for (ChargeStationInfo chargeStationInfo : chargeStationInfos) {
            MyStationRespDto myStationRespDto = new MyStationRespDto();
            myStationRespDto.setStationStatus(ChargingStationStatusEnum.getDesc(chargeStationInfo.getStatus()));
            myStationRespDto.setStationName(chargeStationInfo.getStationName());
            myStationRespDto.setAddress(chargeStationInfo.getAddress());
            data.add(myStationRespDto);
        }
        baseRespPageDto.setTotalCount(page.getTotal());
        baseRespPageDto.setPageSize(baseReqPageDto.getPageSize());
        baseRespPageDto.setPageNum(baseReqPageDto.getPageNum());
        baseRespPageDto.setTotalPage(page.getPages());
        baseRespPageDto.setData(data);
        return CommonResult.success(baseRespPageDto);
    }

    @Override
    public CommonResult<StationCount4ManagementRespDto> count4ManagementIndex() {
        ChargeStationQueryPageReqDto chargeStationQueryPageReqDto = new ChargeStationQueryPageReqDto();
        int countStation = chargeStationInfoMapper.count(chargeStationQueryPageReqDto);
        chargeStationQueryPageReqDto.setStatus(ChargingStationStatusEnum.OPERATING.getStatus());
        int countOnlineStation = chargeStationInfoMapper.count(chargeStationQueryPageReqDto);
        ChargeStationGunPageReqDto chargeStationGunPageReqDto = new ChargeStationGunPageReqDto();
        int countGun = chargeStationGunMapper.count(chargeStationGunPageReqDto);
        int countGunOnline = chargeStationGunMapper.countOnline();
        StationCount4ManagementRespDto stationCount4ManagementRespDto = new StationCount4ManagementRespDto();
        stationCount4ManagementRespDto.setTotalStationCount(countStation);
        stationCount4ManagementRespDto.setOnlineStationCount(countOnlineStation);
        stationCount4ManagementRespDto.setTotalGunCount(countGun);
        stationCount4ManagementRespDto.setOnlineGunCount(countGunOnline);
        return CommonResult.success(stationCount4ManagementRespDto);
    }
}
